package com.iuie.ns.web.tags;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.iuie.ns.frame.config.AppConfigUtil;
import com.iuie.ns.web.exception.IllegalFormatException;
import com.iuie.ns.web.html.HTMLTag;
import com.iuie.ns.web.html.SimpleHTMLTagBuilder;

/**
 * 
 * @description ViewTag
 * @author lj
 * @createTime 2016年8月23日 下午6:04:30
 */
public class ViewTag extends AbstractTag {

	/**
	 * @user lj
	 * @field serialVersionUID
	 */
	private static final long serialVersionUID = -5029844566590390683L;

	private static final Logger logger = LoggerFactory.getLogger(ViewTag.class); // 日志

	/**
	 * 文件常量
	 * 
	 * @author lj
	 * @date 2016年12月11日13:11:12
	 */
	public static final class Constants {

		/** 视图片段的路径 */
		// public static final String VIEW_PATH="config/front/view"; //20170212
		// 没有必要设view文件来存可加载哪些资源

		/** 可选值 */
		public static final String SELECT_PATH = AppConfigUtil.getInstance().getProperty("select.path");

		/** 默认加载项 */
		public static final String DEFAULT_RES_PATH = AppConfigUtil.getInstance().getProperty("default.res.path");
	}

	/** 设置可选的css和js组合 */
	private String res;
	/** 客户端css */
	private String userCSS;
	/** 客户端js */
	private String userJS;

	private String basePath = "";

	/** 延迟加载js，在DOM加载完毕后 */
	private boolean delayLoadJs = true;
	
	private boolean ignoreDefault = false;

	/** 默认需要加载的css/js */
	private static StringBuilder defaultRes = new StringBuilder();

	/** 存储文件select中的数据 存储格式：name=fileName1,fileName2 */
	public static final Map<String, List<String>> selects = new HashMap<String, List<String>>();

	/** 存储文件view中填写的文件名 */
	public static final Set<String> fileNames = new HashSet<String>();
	
	public static final String PROJECT_VERSION = "/" + AppConfigUtil.getInstance().getProperty("project.version");

	static {
		// 依据select文件初始化selects
		new ViewTag.ResLoader(Constants.SELECT_PATH) {
			@Override
			void onReadLine(String lineStr) {
				String[] arr = lineStr.split("=");
				if (arr.length != 2) {
					throw new IllegalFormatException("文件：" + Constants.SELECT_PATH + "中存在错误的格式");
				}
				String key = arr[0];
				String[] sFileNames = arr[1].split(",");
				List<String> list = Arrays.asList(sFileNames);
				fileNames.addAll(list); // 实例化fileNames
				selects.put(key, list);
			}
		};

		logger.debug("fileNames => {}", fileNames);

		// 实例化fileNames
		// new ViewTag.ResLoader(Constants.VIEW_PATH) {
		// @Override
		// void onReadLine(String lineStr) {
		// fileNames.add(lineStr);
		// }
		// };

		// 实例化默认值
		new ViewTag.ResLoader(Constants.DEFAULT_RES_PATH) {
			@Override
			void onReadLine(String lineStr) {
				defaultRes.append(lineStr).append(",");
			}
		};
		if (defaultRes.length() > 0) {
			defaultRes.delete(defaultRes.length() - 1, defaultRes.length());
		}
	}

	/**
	 * @description 资源文件加载器
	 * @author lj
	 * @createTime 2016年12月3日 下午3:56:59
	 */
	private static abstract class ResLoader {
		public ResLoader(String filePath) {
			InputStream in = ViewTag.class.getClassLoader().getResourceAsStream(filePath);
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			String string = null;
			try {
				while ((string = reader.readLine()) != null) {
					if (string.trim().isEmpty())
						break;
					onReadLine(string.trim());
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if (in != null)
						in.close();
					if (reader != null)
						reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		abstract void onReadLine(String lineStr);
	}

	@Override
	public int doStartTag() throws JspException {
		try {
			preScript();// 加载contextPath到javascript环境中，命名为ctx
			
			if (!this.ignoreDefault){
				execute(defaultRes.toString(), SimpleHTMLTagBuilder.CSS);
			}
			execute(res, SimpleHTMLTagBuilder.CSS);
			loadUserCSS();
			// 不延迟到DOM加载完毕时加载js
			if (!delayLoadJs) {
				writeScript();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return TagSupport.EVAL_PAGE;
	}

	@Override
	public int doEndTag() throws JspException {
		try {
			if (delayLoadJs) {
				writeScript();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return TagSupport.EVAL_PAGE;
	}

	private void writeScript() throws IOException{
		if (!this.ignoreDefault) {
			execute(defaultRes.toString(), SimpleHTMLTagBuilder.JS);
		}
		execute(res, SimpleHTMLTagBuilder.JS);
		loadUserJS();
	}
	
	/*
	 * 加载contextPath到javascript环境中，命名为ctx
	 */
	private void preScript() throws IOException {
		HTMLTag tag = new HTMLTag("script");
		tag.addAttribute("type", "text/javascript");
		tag.setTextNode("var ctx = \"" + getBasePath() + "\";");
		tag.setTextNode("var resourceVersion=\"" + getVersion() + "\";");
		String script = tag.toString();
		this.pageContext.getOut().write(script);
	}
	
	private void execute(String css, String type) throws IOException {
		// 构造HTML标签，将CSS/JS资源链接到JSP中
		SimpleHTMLTagBuilder builder = new SimpleHTMLTagBuilder(getBasePath());
		// 加载的文件内容从FilePath.VIEW_PATH路径下的文件中读取
		JspWriter jspWriter = this.pageContext.getOut();

		Collection<String> lst = null;
		if (css == null) {
			// select属性为空时，加载所有文件
			lst = new ArrayList<>();
		} else {
			// 根据select来决定加载哪些文件
			lst = new ArrayList<String>();
			String[] arr = css.split(",");
			for (String str : arr) {
				if (selects.containsKey(str)) {
					lst.addAll(selects.get(str));
				}
			}
		}
		builder.buildMultipleHtmlTag(lst, type);
		// 写到JSP中去
		jspWriter.write(builder.toString());
	}
	
	/**
	 * @description 解析并拼接用户css
	 * @author lj
	 * @date 2016年8月28日 下午5:01:25
	 * @param userCSS2
	 * @throws IOException
	 */
	private void loadUserCSS() throws IOException {
		if (userCSS == null) {
			return;
		}
		// 构造HTML标签，将CSS/JS资源链接到JSP中
		String contextPath = pageContext.getServletContext().getContextPath();
		SimpleHTMLTagBuilder builder = new SimpleHTMLTagBuilder(contextPath);
		
		builder.setCssPath(basePath.isEmpty() ? (PROJECT_VERSION + basePath) : (PROJECT_VERSION + basePath + "css/"));

		String[] styles = userCSS.split(",");
		for (String style : styles) {
			style = style.trim();
			if (!style.endsWith(".css")) {
				style += ".css";
			}
			builder.buildStyleLinkTag(style);
		}
		pageContext.getOut().append(builder.toString());
	}

	/**
	 * @description 解析并拼接用户的javascript
	 * @author lj
	 * @throws IOException
	 * @date 2016年8月28日 下午5:02:09
	 */
	private void loadUserJS() throws IOException {
		// 构造HTML标签，将CSS/JS资源链接到JSP中
		if (userJS == null) {
			return;
		}
		String contextPath = pageContext.getServletContext().getContextPath();
		SimpleHTMLTagBuilder builder = new SimpleHTMLTagBuilder(contextPath);

		builder.setJsPath(basePath.isEmpty() ? (PROJECT_VERSION + basePath) : (PROJECT_VERSION + basePath + "js/"));

		String[] srcs = userJS.split(",");
		for (String src : srcs) {
			src = src.trim();
			if (!src.endsWith(".js")) {
				src += ".js";
			}
			builder.buildJavascriptTag(src); // 拼接script标签
		}
		pageContext.getOut().append(builder.toString());
	}
	
	/**
	 * @param {@link #userCSS}
	 */
	public void setUserCSS(String userCSS) {
		this.userCSS = userCSS;
	}

	/**
	 * @param {@link #userJS}
	 */
	public void setUserJS(String userJS) {
		this.userJS = userJS;
	}

	/**
	 * @param {@link #basePath}
	 */
	public void setBasePath(String basePath) {
		if(!basePath.isEmpty() && !basePath.endsWith("/")){
			basePath = basePath + "/";
		}
		this.basePath = basePath;
	}

	/**
	 * @param {@link #delayLoadJs}
	 */
	public void setDelayLoadJs(boolean delayLoadJs) {
		this.delayLoadJs = delayLoadJs;
	}
	/**
	 * @param {@link #res}
	 */
	public void setRes(String res) {
		this.res = res;
	}
	public boolean isIgnoreDefault() {
		return ignoreDefault;
	}
	public void setIgnoreDefault(boolean ignoreDefault) {
		this.ignoreDefault = ignoreDefault;
	}
}
